package co.fitstart.admin.dao;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dynamic;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.DateUtils;

import co.fitstart.entity.coach.CoachRestType;
import co.fitstart.entity.course.CourseType;
import co.fitstart.entity.course.round.CourseRound;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.course.schedule.ScheduleSmsNotice;
import co.fitstart.entity.course.schedule.ScheduleStatus;
import co.fitstart.entity.principal.Principal;
import co.fitstart.entity.site.RoomType;
import co.fitstart.entity.site.Site;
import co.fitstart.entity.user.User;

@Repository("scheduleDaoSupport")
public class ScheduleDaoSupport {
    
    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    @Dynamic
    private CourseScheduleDao courseScheduleDao;
    
    @Dynamic
    private CoachDao coachDao;
    
    @Dynamic
    private GroupDao groupDao;
    
    /**
     * 
     * @param coachId
     * @param date
     * @param site
     * @param coachRestType
     * @return
     */
    public List<String> listFreeScheduleTime(Long coachId, Date date, Site site, CoachRestType coachRestType) {
        
        Principal coach = genericDaoSupport.load(Principal.class, coachId);
        
        // get all time
        List<String> allTimes = site.getBusinessTimes();
        
        List<String> freeTimes = new ArrayList<String>();
        List<String> unFreeTimes = new ArrayList<String>();
        if(coachRestType.isDuty()){
          unFreeTimes = this.listRestTime(coach, date, new CoachRestType[]{CoachRestType.REST});
        }
        else if(coachRestType.isBusiness()){
          unFreeTimes = this.listUnfreeScheduleTime(coach, date, new CoachRestType[]{CoachRestType.REST, CoachRestType.EXPERIENCE, CoachRestType.CONFERENCE, CoachRestType.TRAINING});
        }
        else if(coachRestType.isExperience()){
          unFreeTimes = this.listUnfreeScheduleTime(coach, date, new CoachRestType[]{CoachRestType.REST, CoachRestType.BUSINESS, CoachRestType.CONFERENCE, CoachRestType.TRAINING});
        }
        else if(coachRestType.isConference()){
          unFreeTimes = this.listUnfreeScheduleTime(coach, date, new CoachRestType[]{CoachRestType.REST, CoachRestType.BUSINESS, CoachRestType.EXPERIENCE, CoachRestType.TRAINING});
        }
        else if(coachRestType.isTraining()){
          unFreeTimes = this.listUnfreeScheduleTime(coach, date, new CoachRestType[]{CoachRestType.REST, CoachRestType.BUSINESS, CoachRestType.EXPERIENCE, CoachRestType.CONFERENCE});
        }
        
        for(String allTime : allTimes) {
            if(!unFreeTimes.contains(allTime)) {
                if (coachRestType.isExperience()){
                    String expEndTime = getExperiencePeriodEndTime(allTime);
                    String expCoachRestTime = getExperienceCoachRestTime(allTime);
                    if(!unFreeTimes.contains(expEndTime) && allTimes.contains(expEndTime) && !unFreeTimes.contains(expCoachRestTime) && allTimes.contains(expCoachRestTime)){
                        freeTimes.add(allTime);
                    }
                }
                else {    
                    freeTimes.add(allTime);
                }
            }
        }
        
        return freeTimes;
    }
    
    public String getExperiencePeriodEndTime(String time){
        String result = new String();
        String[] arr = time.split(":");
        if(arr[1].equalsIgnoreCase("00")) {
            result = arr[0] + ":30";
        } else {
            int hour = Integer.parseInt(arr[0]) + 1;
            result = Integer.toString(hour) + ":00";
        }
        return result;
    }
    
    public String getExperienceCoachRestTime(String time){
        String result = new String();
        String[] arr = time.split(":");

        int hour = Integer.parseInt(arr[0]) + 1;
        result = Integer.toString(hour) + ":" + arr[1];

        return result;
    }
    
    /**
     * 
     * @param coach
     * @param date
     * @param coachRestType
     * @return
     */
    public List<String> listUnfreeScheduleTime(Principal coach, Date date, CoachRestType[] coachRestTypes) {
        List<String> unFreeTimes = new ArrayList<String>();
        
        unFreeTimes.addAll(this.listCourseTime(coach, date));
        
        unFreeTimes.addAll(coachDao.listRestCalendar(coach, date, coachRestTypes));
        return unFreeTimes;
    }
    
    /**
     * 
     * @param coach
     * @param date
     * @param coachRestTypes
     * @return
     */
    public List<String> listRestTime(Principal coach, Date date, CoachRestType[] coachRestTypes) {
        return coachDao.listRestCalendar(coach, date, coachRestTypes);
    }
    
    /**
     * 
     * @param coach
     * @param date
     * @return
     */
    public List<String> listCourseTime(Principal coach, Date date) {
        List<String> courseTimes = new ArrayList<String>();
        List<String> courseHours = courseScheduleDao.listCourseHours(coach, date);
        List<String> scheduleHours = new ArrayList<String>();
        for(String courseHour : courseHours) {
            String[] arr = courseHour.split(":");
            if(arr[1].equalsIgnoreCase("00")) {
                scheduleHours.add(arr[0] + ":30");
            } else {
                int hour = Integer.parseInt(arr[0]) + 1;
                scheduleHours.add(Integer.valueOf(hour) + ":00");
            }
        }
        courseTimes.addAll(courseHours);
        courseTimes.addAll(scheduleHours);
        
        return courseTimes;
    }
    
    /**
     * 
     * @param currentSite
     * @return
     */
    public Map<String, Object> getStartEndHour(Site currentSite) {
        int start = 0;
        int end = 0;
        if(currentSite != null) {
            start = Integer.parseInt(currentSite.getBusinessStart()[0]);
            if (currentSite.getBusinessEnd()[1].equals("30")) {
                end = Integer.parseInt(currentSite.getBusinessEnd()[0]);
            } else {
                end = Integer.parseInt(currentSite.getBusinessEnd()[0])-1;
            }
        } else {
            List<Site> sites = genericDaoSupport.loadAll(Site.class);
            start = Integer.parseInt(sites.get(0).getBusinessStart()[0]);
            end = Integer.parseInt(sites.get(0).getBusinessEnd()[0]);
            for(Site site : sites) {
                int thisStart = Integer.parseInt(site.getBusinessStart()[0]);
                int thisEnd = Integer.parseInt(site.getBusinessEnd()[0]);
                if(thisStart < start) {
                    start = thisStart;
                }
                if(thisEnd > end) {
                    if(site.getBusinessEnd()[1].equals("30")) {
                        end = thisEnd;
                    } else {
                        end = thisEnd-1;
                    }
                }
            }
        }
        
        Map<String, Object> hour = new HashMap<String, Object>();
        hour.put("start", start);
        hour.put("end", end);
        return hour;
    }
    
    /**
     * 
     * @param hour
     * @return
     */
    public List<String> getScheduleObligatoryHours(String hour) {
        List<String> hours = new ArrayList<String>();
        
        Date day = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd") + " " + hour + ":00", "yyyy-MM-dd HH:mm:ss");
        
        hours.add(hour);
        hours.add(DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addMinutes(day, 30), "HH:mm"));
        hours.add(DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addMinutes(day, -30), "HH:mm"));
//        hours.add(DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(day, -1), "HH:mm"));
//        hours.add(DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(day, 1), "HH:mm"));
        
        return hours;
    }
    
    /**
     * 
     * @param day
     * @param hours
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<Principal> listOnScheduleCoachIds(Date day, List<String> hours) {
        
        String querySentence = "SELECT schedule.coach FROM CourseSchedule schedule WHERE schedule.disabled = false AND schedule.coach IS NOT NULL AND schedule.occurDate = :day AND schedule.occurHour in(:hours)";
        
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("day", day);
        parameters.put("hours", hours);
        
        return genericDaoSupport.searchForList(querySentence, parameters);
    }
    
    /**
     * 
     * @param day
     * @param hours
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<Principal> listOnRestCoachIds(Date day, List<String> hours) {
        
        String querySentence = "SELECT rest.coach FROM CoachRestCalendar rest WHERE rest.disabled = false AND rest.coach IS NOT NULL AND rest.restDate = :day AND rest.restHour in(:hours) AND rest.coachRestType != :coachRestType";
        
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("coachRestType", CoachRestType.DUTY);
        parameters.put("day", day);
        parameters.put("hours", hours);
        
        return genericDaoSupport.searchForList(querySentence, parameters);
    }
    

    /**
     * 
     * @param courseSchedule
     */
    public void createScheduleSmsNotice(CourseSchedule courseSchedule) {
        if((courseSchedule.getCourseType().isIndividual() || courseSchedule.getCourseType().isPosture() || courseSchedule.getCourseType().isSpecial() || courseSchedule.getCourseType().isYear()) && !courseSchedule.getCourse().isExperience()) {
            genericDaoSupport.executeHQL("UPDATE ScheduleSmsNotice SET disabled = true where courseSchedule = :courseSchedule", "courseSchedule", courseSchedule);
            
            ScheduleSmsNotice notice = new ScheduleSmsNotice(null, courseSchedule);
            
            genericDaoSupport.save(notice);
        }
    }
    
    /**
     * 
     * @param courseSchedule
     */
    public void disableScheduleSmsNotice(CourseSchedule courseSchedule) {
        ScheduleSmsNotice notice = courseScheduleDao.getScheduleSmsNotice(courseSchedule);
        
        if(notice != null) {
            genericDaoSupport.update(notice.disable());
        }
    }
    
    /**
     * 
     * @param user
     * @return
     */
    @SuppressWarnings("unchecked")
    public CourseSchedule loadLatestExperience(User user) {
        Map<String, Object> parameters = new HashMap<>();

        parameters.put("individual", CourseType.INDIVIDUAL);
        parameters.put("user", user);
        parameters.put("scheduleStatus", Arrays.asList(new ScheduleStatus[]{ScheduleStatus.READY, ScheduleStatus.FINISH, ScheduleStatus.EXPIRY}));
        
        List<CourseSchedule> schedules =  genericDaoSupport.searchForList(
                "FROM CourseSchedule WHERE disabled = false AND courseType = :individual AND course.experience = true"
                + " AND student.user = :user AND scheduleStatus IN (:scheduleStatus) ORDER BY occurDate DESC", parameters);
        
        if(schedules.isEmpty()) {
            return null;
        } else {
            return schedules.get(0);
        }
    }
    
    /**
     * 
     * @param user
     * @return
     */
    public int countRound(User user) {
        Map<String, Object> parameters = new HashMap<>();
        
        parameters.put("user", user);
        return genericDaoSupport.count("FROM CourseRound WHERE disabled = false AND course.experience = false And student.user = :user", parameters);
    }
    
    @SuppressWarnings("unchecked")
    public double sumPrice(User user) {
        List<Long> list = genericDaoSupport.searchForList("SELECT SUM(round.order.price) AS total FROM CourseRound round WHERE round.disabled = false AND round.course.experience = false AND round.order IS NOT NULL AND order.user = :user", "user", user);
        
        if(list.isEmpty() || list.get(0) == null) {
            return 0;
        } else {
            return list.get(0)/100.0;
        }
    }
    
    
    /**
     * 普通课前后半小时内所占用的房间数
     * @param site
     * @param appointDay
     * @param beforeHalfHour
     * @param afterHalfHour
     * @param roomType
     * @return
     */
    public int getUsingRoom(Site site, Date appointDay, String beforeHalfHour,String hour, RoomType roomType) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("roomType", roomType);
        parameters.put("site", site);
        parameters.put("occurDate", appointDay);
        parameters.put("b_occurHour", beforeHalfHour);
        parameters.put("a_occurHour", hour);
        
        return genericDaoSupport.count("FROM CourseSchedule"
                + " WHERE disabled = false AND roomType = :roomType AND site = :site"
                + " AND occurDate = :occurDate AND occurHour  BETWEEN  :b_occurHour AND :a_occurHour", parameters);
    }
    
    
    public int getUsingRoom(Site site, Date appointDay, String beforeHour,String afterHour) {
    	
    	Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("site", site);
        parameters.put("occurDate", appointDay);
        parameters.put("b_occurHour", beforeHour);
        parameters.put("a_occurHour", afterHour);
        
        return genericDaoSupport.count("FROM CoachRestCalendar"
                + " WHERE  restDate = :occurDate AND restHour  BETWEEN  :b_occurHour"
        		+" AND :a_occurHour AND coachRestType = 1 "
                +"AND coach in (SELECT coach FROM PrincipalReportRelation WHERE site = :site)", parameters);
    	
    	
    }
    
    
    /**
     * count using room
     * @param site
     * @param day
     * @param hour
     * @param roomType
     * @return
     */
    public int countUsingRoom(Site site, Date day, String hour, RoomType roomType) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("roomType", roomType);
        parameters.put("site", site);
        parameters.put("occurDate", day);
        parameters.put("occurHour", hour);
        
        return genericDaoSupport.count("FROM CourseSchedule"
                + " WHERE disabled = false AND roomType = :roomType AND site = :site"
                + " AND occurDate = :occurDate AND occurHour = :occurHour", parameters);
    }
    
    /**
     * count using room, except current schedule using room
     * @param site
     * @param day
     * @param hour
     * @param roomType
     * @param currentSchedule
     * @return
     */
    public int countUsingRoom(Site site, Date day, String hour, RoomType roomType, CourseSchedule currentSchedule) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("currentSchedule", currentSchedule);
        parameters.put("roomType", roomType);
        parameters.put("site", site);
        parameters.put("occurDate", day);
        parameters.put("occurHour", hour);
        
        return genericDaoSupport.count("FROM CourseSchedule courseSchedule"
                + " WHERE courseSchedule != :currentSchedule AND courseSchedule.disabled = false"
                + " AND courseSchedule.roomType = :roomType AND courseSchedule.site = :site"
                + " AND courseSchedule.occurDate = :occurDate AND courseSchedule.occurHour = :occurHour", parameters);
    }
    
    /**
     * count using room for individual experience
     * @param site
     * @param day
     * @param hour
     * @param roomType
     * @return
     */
    public int countUsingRoomForExperience(Site site, Date day, String hour, RoomType roomType) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("individual", CourseType.INDIVIDUAL);
        parameters.put("roomType", roomType);
        parameters.put("site", site);
        parameters.put("occurDate", day);
        parameters.put("occurHour", hour);
        
        return genericDaoSupport.count("FROM CourseSchedule"
                + " WHERE disabled = false AND courseType=:individual AND course.experience=true"
                + " AND roomType = :roomType AND site = :site"
                + " AND occurDate = :occurDate AND occurHour = :occurHour", parameters);
    }
    
    /**
     * count using room for individual experience, except current schedule using room
     * @param site
     * @param day
     * @param hour
     * @param roomType
     * @param currentSchedule
     * @return
     */
    public int countUsingRoomForExperience(Site site, Date day, String hour, RoomType roomType, CourseSchedule currentSchedule) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("currentSchedule", currentSchedule);
        parameters.put("individual", CourseType.INDIVIDUAL);
        parameters.put("roomType", roomType);
        parameters.put("site", site);
        parameters.put("occurDate", day);
        parameters.put("occurHour", hour);
        
        return genericDaoSupport.count("FROM CourseSchedule courseSchedule"
                + " WHERE courseSchedule != :currentSchedule AND courseSchedule.disabled = false"
                + " AND courseSchedule.courseType=:individual AND courseSchedule.roomType = :roomType AND courseSchedule.site = :site"
                + " AND courseSchedule.occurDate = :occurDate AND courseSchedule.occurHour = :occurHour", parameters);
    }

    /**
     * 重新计算学员课件完成情况
     * @param courseRound
     */
    public void recalculateStudentCourseware(CourseRound courseRound) {
        if(courseRound.getCourseType().isYear() || ((courseRound.getCourseType().isIndividual() || courseRound.getCourseType().isSpecial() || courseRound.getCourseType().isSpecial()) && !courseRound.getCourse().isExperience())) {
            if(courseRound.getCourseStatus().isReady() || courseRound.getCourseStatus().isHold()
                    || courseRound.getCourseStatus().isExpire() || courseRound.getCourseStatus().isActivate()) {
                //重新计算训练部位完成情况
                CourseSchedule courseSchedule1 = courseScheduleDao.load(courseRound);
                if(courseSchedule1 == null) {
                    genericDaoSupport.update(courseRound.editPlannedContent(0));
                } else {
                    genericDaoSupport.update(courseRound.editPlannedContent(courseScheduleDao.count(courseRound, courseSchedule1.getOccurDate(), courseSchedule1.getOccurHour())));
                }
                
                //重新计算上课课件完成情况
                CourseSchedule courseSchedule2 = groupDao.loadCourseSchedue(courseRound);
                if(courseSchedule2 == null) {
                    genericDaoSupport.update(courseRound.editPlannedCourseware(0));
                } else {
                    genericDaoSupport.update(courseRound.editPlannedCourseware(courseScheduleDao.count(courseRound, courseSchedule2.getOccurDate(), courseSchedule2.getOccurHour())));
                }
            }
        }
    }
 }
